#ifndef __DISPATCH_H__
#define __DISPATCH_H__

#include <stdlib.h>
#include <string>
#include <stdio.h>

// for uint16_t etc
#include <stdint.h>

#ifdef __WIN32__

	#include <winsock2.h>
	#include <WS2tcpip.h>

	typedef u_short in_port_t;
#else

	#define INVALID_SOCKET -1

	#include <sys/socket.h>
	#include <sys/types.h>

	#include <arpa/inet.h>
	#include <netdb.h>
	#include <netinet/in.h>
	#include <fcntl.h>

#endif

#define TEXT_PORT 6716
#define FILE_PORT 6717

typedef enum
{
	CORRUPT,

	// Client notifies server of disconnect
	CLIENT_DISCONNECT,

	// Server notifies client(s) of shutdown
	SERVER_DISCONNECT,

	// DEPRICATED: Implicit on a SERVER_USER_LIST update
	// Server notifies clients of a room join.
	SERVER_NOTIFY_JOIN,

	// Client requests to use a specific username.
	CLIENT_REQUEST_NAME,

	// Client officially requests connection to server.
	CLIENT_REQUEST_CONNECTION,

	// Server notifies clients of client rename
	SERVER_CLIENT_RENAME,

	// Server lists users in room.
	SERVER_USER_LIST_UPDATE,

	// Server lists users in room.
	SERVER_USER_LIST,

	// Tells client uid and username
	SERVER_DICTATE_INFO,

	// Client sent message.
	CLIENT_MESSAGE,

	// Server sent message.
	SERVER_MESSAGE,

	// Client requests to send a file.
	SEND_FILE,

	// When this packet makes it through, the punchthrough is complete
	CLIENT_NAT_SUCCESS,

	// Contains NAT information about requesting client and info about file to be sent
	FILE_SEND_REQUEST,

	// Contains NAT information about accepting client
	FILE_SEND_ACCEPT,

	// Contains information about which file is being rejected
	FILE_SEND_REJECT,

	// Client sends information about file to be sent.
	FILE_HEADER,

	// Part of the file being transfered
	FILE_PART,

	// Acknowledges a file chunk
	FILE_ACK,

} DispatchFlag;

/*
 * Number of bits reserved for data to be sent
 *****************************************************/
#define DATA_SIZE 512

template <typename myType>
size_t encode_data ( char *buffer, myType n)
{
	size_t char_size = sizeof(char);
	size_t uint_size = sizeof(myType);
	size_t i = 0;

	while (uint_size > 0)
	{
		uint_size -= char_size;
		buffer[i] = n >> (uint_size*(8*char_size));
		i++;
	}

	// How many bytes written?
	return sizeof(myType)/sizeof(char);
}

template <typename myType>
size_t decode_data ( const char *buffer, myType *n)
{
	size_t char_size = sizeof(char);
	size_t uint_size = sizeof(myType);
	size_t i = 0;

	(*n) = (myType)0;

	while (uint_size > 0)
	{
		uint_size -= char_size;
		(*n) = (myType)((*n) | (buffer[i] << (uint_size*(8*char_size))));
		i++;
	}

	// How many bytes read?
	return sizeof(myType)/sizeof(char);
}

#ifndef vasprintf
extern int vasprintf (char **resultp, const char *format, va_list arg_list);
#endif

#ifndef asprintf
extern int asprintf (char **resultp, const char *format, ...);
#endif

typedef struct
{
	uint16_t client_uid;
	uintptr_t ft_uid;
	in_port_t port;
	in_addr addr;
	size_t filename_size;
	size_t filesize; // in bytes
} FileTransferHandshake;

typedef struct
{
	uint16_t client_uid;
	uintptr_t ft_uid;
	uint32_t data_offset;
	size_t data_length;
} FileTransferData;
/*
 * The Dispatch class is used to pass information back
 * and forth between two connections
 *****************************************************/
class Dispatch {

	public:
		// Basic constructor
		Dispatch( void );

		// Set flag and data
		Dispatch( DispatchFlag flag );
		Dispatch( DispatchFlag flag, std::string d );
		Dispatch( DispatchFlag flag, const char *d, size_t n );

		~Dispatch( void );

		// Send packet over socket
		bool release( int socket );

		// Receive a packet via socket
		ssize_t receive( int socket );
		ssize_t receive( void );

		// Returns data of packet.
		const std::string getData( void ) const;
		const char* getRawData( void ) const;

		// Return flag of packet.
		const DispatchFlag getFlag( void ) const;

		// Return socket of packet.
		int getSocket( void ) const;

		// Return the length of data in the packet
		size_t getDataLength( void ) const;

		// Set packet flag
		void setFlag( DispatchFlag flag );

		// Set packet data
		void setData( const char *d, size_t n );

	private:
		// Character String holding packet data.
		char *data;

		size_t length;

		// Character string holding flag of packet.
		DispatchFlag flag;

		// Socket
		int socket;
};

#endif
